﻿using FDStudio.Core.Entities;
using FDStudio.Core.Entities.Query;
using FDStudio.Core.Expressions;
using FDStudio.Core.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace FDStudio.Core.Generates
{
    /*
     CreatedOn: 2018-01-26
     CreatedBy: Jackie Lee(天宇遊龍)
     */
    /// <summary>
    /// 映射生成
    /// </summary>
    public class ModelGenerator : IModelGenerator
    {
        private ConditionVisitor visitor = new ConditionVisitor();
        private bool isUpdate;

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        public string UpdateString<T>(Expression<Func<T, bool>> condition)
        {
            isUpdate = true;
            StringBuilder sb = new StringBuilder();
            sb.Append("UPDATE ");
            sb.Append(GetTableName<T>(out string aliasName));
            sb.Append(" SET ");
            var columns = Resolve<T>(out IList<string> parameters, null);
            for (int i = 0; i < columns.Count; ++i)
            {
                sb.AppendFormat("{0}={1},", columns[i], parameters[i]);
            }
            sb.Remove(sb.Length - 1, 1);

            sb.Append(GetCondition(condition));
            sb.Append(";");
            isUpdate = false;

            return sb.ToString();
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        public string QueryString<T>(Expression<Func<T, bool>> condition) where T : class
        {
            return QueryString(condition, null);
        }

        /// <summary>
        /// 含排序查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <param name="orderBy"></param>
        /// <returns></returns>
        public string QueryString<T>(Expression<Func<T, bool>> condition, IQueryOrder<T> orderBy) where T : class
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT ");
            IDictionary<string, string> columnAlias = new Dictionary<string, string>();
            var columns = Resolve<T>(out IList<string> parameters, (colunName, p) =>
            {
                string aliasName = GetAliasName(p);
                if (!aliasName.IsNullOrEmpty() && !columnAlias.ContainsKey(colunName))
                {
                    columnAlias.Add(colunName, aliasName);
                }
            });
            Func<string, string> getName = cname =>
            {
                return columnAlias.ContainsKey(cname) ? $"{cname} AS {columnAlias[cname]}" : cname;
            };
            foreach (var c in columns)
            {
                sb.AppendFormat("{0},", getName(c));
            }
            sb.Remove(sb.Length - 1, 1);

            sb.Append(" FROM ");
            sb.Append(GetTableName<T>(out string aliasTabName));
            if (!aliasTabName.IsNullOrEmpty())
            {
                sb.Append($" AS {aliasTabName}");
            }

            sb.Append(GetCondition(condition));

            sb.Append($" {orderBy?.Result}");

            sb.Append(";");
            return sb.ToString();
        }

        /// <summary>
        /// 计数查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <returns></returns>
        public string CountString<T>(Expression<Func<T, bool>> condition)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT COUNT(1) AS COUNT FROM ");
            sb.Append(GetTableName<T>(out string aliasTabName));
            sb.Append(GetCondition(condition));

            return sb.ToString();
        }

        /// <summary>
        /// 插入
        /// </summary>
        /// <returns></returns>
        public string InsertString<T>(T model, out IDictionary<string, object> parametes)
        {
            StringBuilder sb = new StringBuilder();
            IDictionary<string, string> columAlias = new Dictionary<string, string>();
            var columns = Resolve<T>(out IList<string> parameters, null).Where(c => c != "Id");
            parameters.Remove("@Id");
            sb.Append("INSERT INTO ");
            sb.Append(GetTableName<T>(out string aliasName));
            sb.AppendFormat("({0})", string.Join(',', columns));
            sb.Append(" VALUES (");
            sb.Append(string.Join(',', parameters));
            sb.Append(");");
            parametes = CreateParameters(model, columns);

            return sb.ToString();
        }

        /// <summary>
        /// 创建对应参数数典
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <param name="parameters">所需要参数名</param>
        /// <returns></returns>
        private IDictionary<string, object> CreateParameters<T>(T model, IEnumerable<string> parameters)
        {
            var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            IDictionary<string, object> result = new Dictionary<string, object>();
            foreach (var p in parameters)
            {
                var pro = props.Where(pr => pr.Name == p).FirstOrDefault();
                if (pro == null)
                    continue;
                if (result.ContainsKey(p))
                    continue;
                result.Add(p, pro.GetValue(model));
            }
            return result;
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        public string DeleteString<T>(Expression<Func<T, bool>> condition)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("DELETE FROM ");
            sb.Append(GetTableName<T>(out string aliasName));
            sb.Append(GetCondition(condition));
            sb.Append(";");
            return sb.ToString();
        }

        /// <summary>
        /// 查询条件
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        private string GetCondition<T>(Expression<Func<T, bool>> condition)
        {
            var strCondition = visitor.Resolve(condition, m =>
            {
                string nodeName = m.Member.Name;
                if (m.Member.DeclaringType == typeof(T))
                {
                    PropertyInfo pi = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList().FirstOrDefault(p => p.Name == nodeName);
                    string cName = GetColumnName(pi);
                    return cName.IsNullOrEmpty() ? m.Member.Name : cName;
                }
                return nodeName;
            }, null);
            if (!strCondition.IsNullOrEmpty())
            {
                return $" WHERE {strCondition}";
            }
            return string.Empty;
        }

        /// <summary>
        /// 解析类型到列
        /// </summary>
        /// <param name="columnName">解析列名时，个性化命名</param>
        /// <param name="resolveProperty">解析回调</param>
        /// <returns></returns>
        private IList<string> Resolve<T>(out IList<string> parameters, Action<string, PropertyInfo> resolveProperty)
        {
            var type = typeof(T);
            var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
            var columns = new List<string>();
            parameters = new List<string>();
            foreach (var p in props)
            {
                if (IsIgnore(p))
                    continue;
                if (isUpdate && IsIgnoreForUpdate(p))
                {
                    continue;
                }
                var cname = GetColumnName(p);
                if (cname.IsNullOrEmpty())
                {
                    cname = p.Name;
                }
                columns.Add(cname);
                parameters.Add($"@{cname}");
                resolveProperty?.Invoke(cname, p);
            }
            return columns;
        }

        /// <summary>
        /// 忽略项
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private bool IsIgnore(PropertyInfo property)
        {
            return null != property.GetCustomAttribute<IgnoreAttribute>(true);
        }

        /// <summary>
        /// 更新忽略主键
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private bool IsIgnoreForUpdate(PropertyInfo property)
        {
            return null != property.GetCustomAttribute<KeyAttribute>(true) ||
            property.Name.Equals("Id"); // Id 为默认主键
        }

        /// <summary>
        /// 列别名
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private string GetAliasName(PropertyInfo property)
        {
            return property.GetCustomAttribute<AliasAttribute>(true)?.Name;
        }

        /// <summary>
        /// 列名
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        private string GetColumnName(PropertyInfo property)
        {
            return property.GetCustomAttribute<ColumnAttribute>(true)?.Name;
        }

        private void AddAalias(IDictionary<string, string> alias, PropertyInfo property)
        {
            var name = GetAliasName(property);
            if (name.IsNullOrEmpty())
                return;
            if (alias.ContainsKey(property.Name))
                return;
            alias.Add(property.Name, name);
        }

        /// <summary>
        /// 获取表名
        /// </summary>
        /// <param name="aliasName">别名</param>
        /// <returns>表名</returns>
        private string GetTableName<T>(out string aliasName)
        {
            var ta = typeof(T).GetCustomAttribute<TableAttribute>();
            var tas = typeof(T).GetCustomAttribute<AliasAttribute>();
            aliasName = string.Empty;
            if (tas != null)
            {
                aliasName = tas.Name;
            }
            if (ta == null)
            {
                return typeof(T).Name;
            }
            return ta.Name;
        }
    }
}